﻿using System;
using Wizard.Source;

namespace Wizard.Runtime
{
    // thred is only visible to the outside through ThreadStep
    internal sealed class Thread
    {
        private static int sequence;
        private readonly int mRunningMethodId;
        private readonly IMethodSource mStartMethod;
        private ThreadStep mCurrent;

        public Thread(IClassInstance instance, IMethodSource method)
        {
            sequence++;
            mRunningMethodId = sequence;
            mStartMethod = method;
            //NOT_IMPLEMENTED
            //mCurrent = new ThreadStep(this, null, null,
            //    new BlockInstance(method.MainBlock),
            //    0, RunningState.BeforeRun);
        }

        public IMethodSource StartMethod
        {
            get { return mStartMethod; }
        }

        public IWizardEngine Engine
        {
            get { return mStartMethod.Engine; }
        }

        internal ThreadStep Current
        {
            get { return mCurrent; }
        }

        public int RunningMethodId
        {
            get { return mRunningMethodId; }
        }

        public int CurrentPc
        {
            get
            {
                if (mCurrent == null) return -1;
                //return mCurrent.Pc;
                return 0;
            }
        }


        public void Gosub(int newPc)
        {
            throw new NotImplementedException();
        }

        public void Pop()
        {
            throw new NotImplementedException();
        }

        public void MoveTo(ThreadStep newState, object applicationContext)
        {
        }

        public void StepForward(object applicationContext)
        {
            //if (mCurrent.RunningState == RunningState.Finished) throw new Exception("Thread is finished.");
            //Statements.Statement statement;

            //switch (mCurrent.RunningState)
            //{
            //    case RunningState.BeforeRun:

            //        var statementArgs = new IStatementArgs(this, applicationContext);

            //        statement = mCurrent.GetStatement();
            //        if (statement == null)
            //        {
            //            var parentStep = mCurrent.ParentThreadStep;
            //            if (parentStep == null)
            //            {
            //                mCurrent = new ThreadStep(this, mCurrent, null, mCurrent.BlockInstance, mCurrent.Pc, RunningState.Finished);
            //            }
            //            else
            //            {
            //                mCurrent = new ThreadStep(this, mCurrent, parentStep.ParentThreadStep, parentStep.BlockInstance, parentStep.Pc, RunningState.InRun);
            //            }
            //            return;
            //        }

            //        RunArgs runArgs = new RunArgs();
            //        runArgs.RunResult = RunResult.Continue;

            //        try
            //        {
            //            statement.OnRun(statementArgs, runArgs);
            //        }
            //        catch (Exception ex)
            //        {
            //            runArgs.RunResult = RunResult.CantRun;
            //            DisplayException(ex);
            //        }
            //        finally
            //        {

            //        }
            //        bool waitForUser = false;
            //        switch (runArgs.RunResult)
            //        {
            //            case RunResult.CantRun:
            //                // rollback;
            //                GoBackToLastInput();
            //                return;
            //            case RunResult.GoSub:
            //                if (runArgs.Sub == null) goto case RunResult.Continue;
            //                //NOT_IMPLEMENTED
            //                //mCurrent = new ThreadStep(this, mCurrent, mCurrent, new BlockInstance(runArgs.Sub), 0, RunningState.BeforeRun);
                            
            //                //Stack
            //                //else line = Method.MainBlock.GetStatement(Pc);
            //                //Pc = runArgs.Sub;
            //                break;
            //            case RunResult.Continue:
            //                mCurrent = new ThreadStep(this, mCurrent, mCurrent.ParentThreadStep, mCurrent.BlockInstance, mCurrent.Pc, RunningState.AfterRun);
            //                break;
            //            case RunResult.WaitForUserAction:
            //                waitForUser = true;
            //                break;
            //        }
            //        break;
            //    case RunningState.InRun:
            //        statement = mCurrent.GetStatement();

            //        statementArgs = new IStatementArgs(this, applicationContext);
            //        statement = mCurrent.GetStatement();

            //        if (statement != null)
            //        {
            //            SubCompleteArgs leaveForwardArgs = new SubCompleteArgs();
            //            leaveForwardArgs.SubCompleteResult = SubCompleteResult.Continue;

            //            try
            //            {
            //                statement.OnSubComplete(statementArgs, leaveForwardArgs);
            //            }
            //            catch (Exception ex)
            //            {
            //                leaveForwardArgs.SubCompleteResult = SubCompleteResult.CantLeave;
            //                DisplayException(ex);

            //                throw;
            //            }

            //            switch (leaveForwardArgs.SubCompleteResult)
            //            {
            //                case SubCompleteResult.Continue:
            //                    //Pc++;
            //                    mCurrent = new ThreadStep(this, mCurrent, mCurrent.ParentThreadStep, mCurrent.BlockInstance, mCurrent.Pc, RunningState.AfterRun);
            //                    break;
            //                case SubCompleteResult.GoSub:
            //                    //Pop();

            //                    break;

            //                case SubCompleteResult.WaitForUserAction:
            //                    mCurrent = new ThreadStep(this, mCurrent, mCurrent.ParentThreadStep, mCurrent.BlockInstance, mCurrent.Pc, RunningState.InRun);
            //                    return;
            //            }

            //        }

            //        break;
            //    case RunningState.AfterRun:
            //        int pc = mCurrent.Pc;
            //        pc++;
            //        mCurrent = new ThreadStep(this, mCurrent, mCurrent.ParentThreadStep, mCurrent.BlockInstance, pc, RunningState.BeforeRun);

            //        //Pc++;
            //        //if (Pc>...)
            //        //{
            //        //    mRunningState = RunningState.BeforeEnter;
            //        //}
            //        //else
            //        //{
            //        //    if (mScripEngine.mInstanceStack.Count > 0)
            //        //    {
            //        //        mScripEngine.mBlockInstance = mScripEngine.mInstanceStack.Pop();
            //        //    }
            //        //    else mScripEngine.mBlockInstance.mRunningState = RunningState.Finished;
            //        //}
            //        break;
            //}

            ////StatementSource line;

            ////line = Method.MainBlock.GetStatement(Pc);

            ////if (line == null)
            ////{
            ////    mIsRunning = false;
            ////    mMethod.Application.MethodIsFinished(statementArgs);
            ////    mCurrent = new ThreadStep(this, mCurrent, Pc, RunningState.Finished, false);
            ////    break;
            ////}

            ////mCurrent = new ThreadStep(this, mCurrent, Pc, RunningState.BeforeLeave, waitForUser);
            ////if (waitForUser) return;
            ////}
            ////"runmethodid"
        }

        private void GoBackToLastInput()
        {
            throw new NotImplementedException();
        }

        private void DisplayException(Exception ex)
        {
            throw new NotImplementedException();
        }

        public void RunBackward(object applicationContext)
        {
            //if (mCurrent.RunningState == RunningState.Finished) throw new Exception("Thread is finished.");
            //var current = mCurrent;

            //while (current != null)
            //{
            //    var method = mStartMethod;
            //    var line = method.MainBlock.GetStatement(current.Pc);
            //    StatementArgs statementArgs = null;
            //    IStatement statement = null;
            //    if (line != null)
            //    {
            //        statementArgs = new StatementArgs(this, applicationContext);
            //        statement = line.Instantiate(mCurrent.BlockInstance, statementArgs);
            //        statement.LeaveBackward(statementArgs);
            //        current = current.PreviousThreadStep;
            //    }
            //    if (current == null)
            //    {
            //        mCurrent = null;
            //        //var StatementArgs = new StatementArgs(runningMethod, applicationContext);
            //        //mApplication.MethodIsClosed(StatementArgs);
            //        return;
            //    }

            //    line = method.MainBlock.GetStatement(current.Pc);
            //    if (line != null)
            //    {
            //        statementArgs = new StatementArgs(this, applicationContext);
            //        statement = line.Instantiate(mCurrent.BlockInstance, statementArgs);
            //        UndoArgs args = new UndoArgs();
            //        args.undoResult = undoResult.Continue;
            //        statement.Undo(statementArgs, args);
            //        if (current.WaitForUser) break;
            //    }
            //}
            //if (current != null)
            //{
            //    mCurrent = current;
            //}
        }

        public void MoveTo(int pc, object applicationContext)
        {
            //if (mCurrent.RunningState == RunningState.Finished) throw new Exception("Thread is finished.");
            //var search = mCurrent;
            //while (mCurrent != null && mCurrent.Pc > pc)
            //{
            //    RunBackward(applicationContext);
            //}
            //while (mCurrent != null && mCurrent.Pc < pc)
            //{
            //    StepForward(applicationContext);
            //}
            //if (mCurrent.Pc != pc)
            //{
            //}
        }

        internal bool IsRunning()
        {
            //return (mCurrent.RunningState != RunningState.Finished);
            return true;
        }
    }
}